﻿/********************************************************************
	created:	2011-8-15   15:54
	author:		Lifan su
	
	purpose:	implement of MD-5 functions
*********************************************************************/

#include "md5.h"
#include <cvt_int_mem.h>
#include "hex_to_char.h"

#include <assert.h>
#include <_stdint.h>
#include <string.h>


static const int blocksize = 0x40;
static const int blockmask = 0x3F;
static const int endmaxsize = 0x37;
static const uint8_t paddingch1 = 0x80;
static const uint8_t paddingcha = 0x00;


static const uint32_t md5_table[] = {
    UINT32_C(0xd76aa478), UINT32_C(0xe8c7b756), UINT32_C(0x242070db), UINT32_C(0xc1bdceee),
    UINT32_C(0xf57c0faf), UINT32_C(0x4787c62a), UINT32_C(0xa8304613), UINT32_C(0xfd469501),
    UINT32_C(0x698098d8), UINT32_C(0x8b44f7af), UINT32_C(0xffff5bb1), UINT32_C(0x895cd7be),
    UINT32_C(0x6b901122), UINT32_C(0xfd987193), UINT32_C(0xa679438e), UINT32_C(0x49b40821),

    UINT32_C(0xf61e2562), UINT32_C(0xc040b340), UINT32_C(0x265e5a51), UINT32_C(0xe9b6c7aa),
    UINT32_C(0xd62f105d), UINT32_C(0x02441453), UINT32_C(0xd8a1e681), UINT32_C(0xe7d3fbc8),
    UINT32_C(0x21e1cde6), UINT32_C(0xc33707d6), UINT32_C(0xf4d50d87), UINT32_C(0x455a14ed),
    UINT32_C(0xa9e3e905), UINT32_C(0xfcefa3f8), UINT32_C(0x676f02d9), UINT32_C(0x8d2a4c8a),

    UINT32_C(0xfffa3942), UINT32_C(0x8771f681), UINT32_C(0x6d9d6122), UINT32_C(0xfde5380c),
    UINT32_C(0xa4beea44), UINT32_C(0x4bdecfa9), UINT32_C(0xf6bb4b60), UINT32_C(0xbebfbc70),
    UINT32_C(0x289b7ec6), UINT32_C(0xeaa127fa), UINT32_C(0xd4ef3085), UINT32_C(0x04881d05),
    UINT32_C(0xd9d4d039), UINT32_C(0xe6db99e5), UINT32_C(0x1fa27cf8), UINT32_C(0xc4ac5665),

    UINT32_C(0xf4292244), UINT32_C(0x432aff97), UINT32_C(0xab9423a7), UINT32_C(0xfc93a039),
    UINT32_C(0x655b59c3), UINT32_C(0x8f0ccc92), UINT32_C(0xffeff47d), UINT32_C(0x85845dd1),
    UINT32_C(0x6fa87e4f), UINT32_C(0xfe2ce6e0), UINT32_C(0xa3014314), UINT32_C(0x4e0811a1),
    UINT32_C(0xf7537e82), UINT32_C(0xbd3af235), UINT32_C(0x2ad7d2bb), UINT32_C(0xeb86d391)
};

static uint32_t rotl(uint32_t val, uint32_t n);

static uint32_t bit_op_f(uint32_t x, uint32_t y, uint32_t z);
static uint32_t bit_op_g(uint32_t x, uint32_t y, uint32_t z);
static uint32_t bit_op_h(uint32_t x, uint32_t y, uint32_t z);
static uint32_t bit_op_i(uint32_t x, uint32_t y, uint32_t z);

static void round_op_f(uint32_t *buf_arr, uint32_t *words, uint16_t a,
    uint16_t b, uint16_t c, uint16_t d, uint16_t k, uint16_t s, uint16_t i);
static void round_op_g(uint32_t *buf_arr, uint32_t *words, uint16_t a,
    uint16_t b, uint16_t c, uint16_t d, uint16_t k, uint16_t s, uint16_t i);
static void round_op_h(uint32_t *buf_arr, uint32_t *words, uint16_t a,
    uint16_t b, uint16_t c, uint16_t d, uint16_t k, uint16_t s, uint16_t i);
static void round_op_i(uint32_t *buf_arr, uint32_t *words, uint16_t a,
    uint16_t b, uint16_t c, uint16_t d, uint16_t k, uint16_t s, uint16_t i);

static void block_calc(struct MD5_c *val, const void *buf);


CRYPT_HASH_PREFIX void CRYPT_HASH_API md5_d_read(struct MD5_d *dst, const void *src)
{
    const uint8_t *_src = src;

    assert(dst && src && "Non-null pointer");

    dst->hash[0] = cvt_byte_to_u32_be(_src +  0);
    dst->hash[1] = cvt_byte_to_u32_be(_src +  4);
    dst->hash[2] = cvt_byte_to_u32_be(_src +  8);
    dst->hash[3] = cvt_byte_to_u32_be(_src + 12);
}

CRYPT_HASH_PREFIX void CRYPT_HASH_API md5_d_write(void *dst, const struct MD5_d *src)
{
    uint8_t *_dst = dst;

    assert(dst && src && "Non-null pointer");

    cvt_u32_be_to_byte(_dst +  0, src->hash[0]);
    cvt_u32_be_to_byte(_dst +  4, src->hash[1]);
    cvt_u32_be_to_byte(_dst +  8, src->hash[2]);
    cvt_u32_be_to_byte(_dst + 12, src->hash[3]);
}

CRYPT_HASH_PREFIX void CRYPT_HASH_API md5_d_print(char *dst, const struct MD5_d *src)
{
    int i, j;

    assert(dst && src && "Non-null pointer");

    for (i = 0; i != 4; ++i) {
        for (j = 8; j != 0; --j) {
            *(dst++) = hex_to_char(((src->hash[i]) >> (4 * j - 4)) & 0xF);
        }
    }

    *dst = '\0';
}

CRYPT_HASH_PREFIX void CRYPT_HASH_API md5_d_printw(wchar_t *dst, const struct MD5_d *src)
{
    int i, j;

    assert(dst && src && "Non-null pointer");

    for (i = 0; i != 4; ++i) {
        for (j = 8; j != 0; --j) {
            *(dst++) = hex_to_wchar(((src->hash[i]) >> (4 * j - 4)) & 0xF);
        }
    }

    *dst = L'\0';
}

CRYPT_HASH_PREFIX uint8_t CRYPT_HASH_API md5_d_less(const struct MD5_d *lhs, const struct MD5_d *rhs)
{
    assert(lhs && rhs && "Non-null pointer");

    return (lhs->hash[0] < rhs->hash[0]) || ((lhs->hash[0] == rhs->hash[0]) 
        && ((lhs->hash[1] < lhs->hash[1]) || ((lhs->hash[1] == rhs->hash[1])
        && ((lhs->hash[2] < rhs->hash[2]) || ((lhs->hash[2] == rhs->hash[2])
        && ((lhs->hash[3] < rhs->hash[3])))))));
}

CRYPT_HASH_PREFIX uint8_t CRYPT_HASH_API md5_d_eq(const struct MD5_d *lhs, const struct MD5_d *rhs)
{
    assert(lhs && rhs && "Non-null pointer");

    return (lhs->hash[0] == rhs->hash[0]) && (lhs->hash[1] == rhs->hash[1])
        && (lhs->hash[2] == rhs->hash[2]) && (lhs->hash[3] == rhs->hash[3]);
}

CRYPT_HASH_PREFIX void CRYPT_HASH_API md5_cvt(struct MD5_d *dst, const struct MD5_c *src)
{
    int i;

    assert(src && dst && "Non-null pointer");

    for (i = 0; i != 4; ++i) {
        dst->hash[i] = src->hash[i];
    }
}

CRYPT_HASH_PREFIX void CRYPT_HASH_API md5_c_init(struct MD5_c *val)
{
    assert(val && "Non-null pointer");

    val->hash[0] = UINT32_C(0x67452301);
    val->hash[1] = UINT32_C(0xefcdab89);
    val->hash[2] = UINT32_C(0x98badcfe);
    val->hash[3] = UINT32_C(0x10325476);

    val->length = 0;
    val->append = 1;
}

CRYPT_HASH_PREFIX void CRYPT_HASH_API md5_c_append(struct MD5_c *val, const void *src, uint64_t len)
{
    int buffer_used;
    const uint8_t *_src = src;

    assert(val && src && val->append && "Non-null pointer and append able");

    buffer_used = val->length & blockmask;
    val->length += len;
    while (len >= blocksize - buffer_used) {
        if (buffer_used) {
            memcpy(val->buffer + buffer_used, _src, blocksize - buffer_used);
            block_calc(val, val->buffer);
            _src += (blocksize - buffer_used);
            len -= (blocksize - buffer_used);
            buffer_used = 0;
        } else {
            block_calc(val, _src);
            _src += blocksize;
            len -= blocksize;
        }
    }
    
    memcpy(val->buffer + buffer_used, _src, (size_t)len);
}

CRYPT_HASH_PREFIX void CRYPT_HASH_API md5_c_close(struct MD5_c *val)
{
    int i;

    assert(val && val->append && "Non-null pointer and append able");

    if ((val->length & blockmask) <= endmaxsize) {
        val->buffer[val->length & blockmask] = paddingch1;
        memset(val->buffer + (val->length & blockmask) + 1, paddingcha,
            endmaxsize - (val->length & blockmask));
        cvt_u64_le_to_byte(val->buffer + endmaxsize + 1, val->length << 3);
        block_calc(val, val->buffer);
    } else {
        val->buffer[val->length & blockmask] = paddingch1;
        memset(val->buffer + (val->length & blockmask) + 1, paddingcha,
            blocksize - 1 - (val->length & blockmask));
        block_calc(val, val->buffer);
        memset(val->buffer,  paddingcha, blocksize - 8);
        cvt_u64_le_to_byte(val->buffer + endmaxsize + 1, val->length << 3);
        block_calc(val, val->buffer);
    }

    for (i = 0; i != 4; ++i) {
        val->hash[i] = cvt_u32_endian(val->hash[i]);
    }

    val->append = 0;
}


static uint32_t rotl(uint32_t val, uint32_t n)
{
    return (val << n) | (val >> (32 - n));
}

static uint32_t bit_op_f(uint32_t x, uint32_t y, uint32_t z)
{
    return ((x & y) | (~x & z));
}

static uint32_t bit_op_g(uint32_t x, uint32_t y, uint32_t z)
{
    return ((x & z) | (y & ~z));
}

static uint32_t bit_op_h(uint32_t x, uint32_t y, uint32_t z)
{
    return (x ^ y ^ z);
}

static uint32_t bit_op_i(uint32_t x, uint32_t y, uint32_t z)
{
    return (y ^(x | ~z));
}

static void round_op_f(uint32_t *buf_arr, uint32_t *words, uint16_t a, uint16_t b,
    uint16_t c, uint16_t d, uint16_t k, uint16_t s, uint16_t i)
{
    buf_arr[a] = buf_arr[b]
    + rotl(buf_arr[a] + words[k] + md5_table[i] 
    + bit_op_f(buf_arr[b], buf_arr[c], buf_arr[d]),
        s);
}

static void round_op_g(uint32_t *buf_arr, uint32_t *words, uint16_t a, uint16_t b,
    uint16_t c, uint16_t d, uint16_t k, uint16_t s, uint16_t i)
{
    buf_arr[a] = buf_arr[b]
    + rotl(buf_arr[a] + words[k] + md5_table[i] 
    + bit_op_g(buf_arr[b], buf_arr[c], buf_arr[d]),
        s);
}

static void round_op_h(uint32_t *buf_arr, uint32_t *words, uint16_t a, uint16_t b,
    uint16_t c, uint16_t d, uint16_t k, uint16_t s, uint16_t i)
{
    buf_arr[a] = buf_arr[b]
    + rotl(buf_arr[a] + words[k] + md5_table[i] 
    + bit_op_h(buf_arr[b], buf_arr[c], buf_arr[d]),
        s);
}

static void round_op_i(uint32_t *buf_arr, uint32_t *words, uint16_t a, uint16_t b,
    uint16_t c, uint16_t d, uint16_t k, uint16_t s, uint16_t i)
{
    buf_arr[a] = buf_arr[b]
    + rotl(buf_arr[a] + words[k] + md5_table[i] 
    + bit_op_i(buf_arr[b], buf_arr[c], buf_arr[d]),
        s);
}


void block_calc(struct MD5_c *val, const void *src)
{
    uint32_t i;
    uint32_t words_array[16];
    uint32_t buf_array[4];
    const uint8_t *_src = src;

    for (i = 0; i != 16; ++i) {
        words_array[i] = cvt_byte_to_u32_le(_src + i * 4);
    }
    for (i = 0; i != 4; ++i) {
        buf_array[i] = val->hash[i];
    }

    round_op_f(buf_array, words_array, 0, 1, 2, 3,  0,  7,  0);
    round_op_f(buf_array, words_array, 3, 0, 1, 2,  1, 12,  1);
    round_op_f(buf_array, words_array, 2, 3, 0, 1,  2, 17,  2);
    round_op_f(buf_array, words_array, 1, 2, 3, 0,  3, 22,  3);
    round_op_f(buf_array, words_array, 0, 1, 2, 3,  4,  7,  4);
    round_op_f(buf_array, words_array, 3, 0, 1, 2,  5, 12,  5);
    round_op_f(buf_array, words_array, 2, 3, 0, 1,  6, 17,  6);
    round_op_f(buf_array, words_array, 1, 2, 3, 0,  7, 22,  7);
    round_op_f(buf_array, words_array, 0, 1, 2, 3,  8,  7,  8);
    round_op_f(buf_array, words_array, 3, 0, 1, 2,  9, 12,  9);
    round_op_f(buf_array, words_array, 2, 3, 0, 1, 10, 17, 10);
    round_op_f(buf_array, words_array, 1, 2, 3, 0, 11, 22, 11);
    round_op_f(buf_array, words_array, 0, 1, 2, 3, 12,  7, 12);
    round_op_f(buf_array, words_array, 3, 0, 1, 2, 13, 12, 13);
    round_op_f(buf_array, words_array, 2, 3, 0, 1, 14, 17, 14);
    round_op_f(buf_array, words_array, 1, 2, 3, 0, 15, 22, 15);

    round_op_g(buf_array, words_array, 0, 1, 2, 3,  1,  5, 16);
    round_op_g(buf_array, words_array, 3, 0, 1, 2,  6,  9, 17);
    round_op_g(buf_array, words_array, 2, 3, 0, 1, 11, 14, 18);
    round_op_g(buf_array, words_array, 1, 2, 3, 0,  0, 20, 19);
    round_op_g(buf_array, words_array, 0, 1, 2, 3,  5,  5, 20);
    round_op_g(buf_array, words_array, 3, 0, 1, 2, 10,  9, 21);
    round_op_g(buf_array, words_array, 2, 3, 0, 1, 15, 14, 22);
    round_op_g(buf_array, words_array, 1, 2, 3, 0,  4, 20, 23);
    round_op_g(buf_array, words_array, 0, 1, 2, 3,  9,  5, 24);
    round_op_g(buf_array, words_array, 3, 0, 1, 2, 14,  9, 25);
    round_op_g(buf_array, words_array, 2, 3, 0, 1,  3, 14, 26);
    round_op_g(buf_array, words_array, 1, 2, 3, 0,  8, 20, 27);
    round_op_g(buf_array, words_array, 0, 1, 2, 3, 13,  5, 28);
    round_op_g(buf_array, words_array, 3, 0, 1, 2,  2,  9, 29);
    round_op_g(buf_array, words_array, 2, 3, 0, 1,  7, 14, 30);
    round_op_g(buf_array, words_array, 1, 2, 3, 0, 12, 20, 31);

    round_op_h(buf_array, words_array, 0, 1, 2, 3,  5,  4, 32);
    round_op_h(buf_array, words_array, 3, 0, 1, 2,  8, 11, 33);
    round_op_h(buf_array, words_array, 2, 3, 0, 1, 11, 16, 34);
    round_op_h(buf_array, words_array, 1, 2, 3, 0, 14, 23, 35);
    round_op_h(buf_array, words_array, 0, 1, 2, 3,  1,  4, 36);
    round_op_h(buf_array, words_array, 3, 0, 1, 2,  4, 11, 37);
    round_op_h(buf_array, words_array, 2, 3, 0, 1,  7, 16, 38);
    round_op_h(buf_array, words_array, 1, 2, 3, 0, 10, 23, 39);
    round_op_h(buf_array, words_array, 0, 1, 2, 3, 13,  4, 40);
    round_op_h(buf_array, words_array, 3, 0, 1, 2,  0, 11, 41);
    round_op_h(buf_array, words_array, 2, 3, 0, 1,  3, 16, 42);
    round_op_h(buf_array, words_array, 1, 2, 3, 0,  6, 23, 43);
    round_op_h(buf_array, words_array, 0, 1, 2, 3,  9,  4, 44);
    round_op_h(buf_array, words_array, 3, 0, 1, 2, 12, 11, 45);
    round_op_h(buf_array, words_array, 2, 3, 0, 1, 15, 16, 46);
    round_op_h(buf_array, words_array, 1, 2, 3, 0,  2, 23, 47);

    round_op_i(buf_array, words_array, 0, 1, 2, 3,  0,  6, 48);
    round_op_i(buf_array, words_array, 3, 0, 1, 2,  7, 10, 49);
    round_op_i(buf_array, words_array, 2, 3, 0, 1, 14, 15, 50);
    round_op_i(buf_array, words_array, 1, 2, 3, 0,  5, 21, 51);
    round_op_i(buf_array, words_array, 0, 1, 2, 3, 12,  6, 52);
    round_op_i(buf_array, words_array, 3, 0, 1, 2,  3, 10, 53);
    round_op_i(buf_array, words_array, 2, 3, 0, 1, 10, 15, 54);
    round_op_i(buf_array, words_array, 1, 2, 3, 0,  1, 21, 55);
    round_op_i(buf_array, words_array, 0, 1, 2, 3,  8,  6, 56);
    round_op_i(buf_array, words_array, 3, 0, 1, 2, 15, 10, 57);
    round_op_i(buf_array, words_array, 2, 3, 0, 1,  6, 15, 58);
    round_op_i(buf_array, words_array, 1, 2, 3, 0, 13, 21, 59);
    round_op_i(buf_array, words_array, 0, 1, 2, 3,  4,  6, 60);
    round_op_i(buf_array, words_array, 3, 0, 1, 2, 11, 10, 61);
    round_op_i(buf_array, words_array, 2, 3, 0, 1,  2, 15, 62);
    round_op_i(buf_array, words_array, 1, 2, 3, 0,  9, 21, 63);

    for (i = 0; i != 4; ++i) {
        val->hash[i] += buf_array[i];
    }
}